package cn.agrael.collection;

import org.jiopi.framework.annotation.module.RegisterModule;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.ref.Reference;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import cn.agrael.ref.FinalizablePhantomReference;
import cn.agrael.ref.FinalizableReference;
import cn.agrael.ref.FinalizableSoftReference;
import cn.agrael.ref.FinalizableWeakReference;

/**
 * 支持各种引用类型的 Map。
 * <p>
 * 引用类型请参考 {@link java.lang.ref}与 {@link cn.agrael.ref} 的描述。
 * </p>
 * <p>
 * 该 Map 的特性和 {@link ConcurrentHashMap} 一样。在第一次调用 put/putAll
 * 方法时(仅仅是在同一个类加载器中的第一次
 * ，如果为同一类加载器，那么在put/putAll后，新创建的实例的put/putAll也不算做第一次)，会有一个后台线程随之创建并启动
 * ，该后台线程主要作用和垃圾回收交互，进行回收对象的清理。在清理 键
 * 的同时，会一同清理掉 键的引用包装器 以及 值 和 值的引用包装器 以及 印射 ，在清理 值 时 ，会一同清理掉 值的引用包装器，然后从 Map 中移除印射到自身的 键 以及 印射 。
 * </p>
 * <p>
 * 该清理是 异步 且 “即时” 的,会在垃圾回收的同时清理，不需要在下次对该 Map 操作时才清理包装引用的实例。
 * </p>
 * 
 * @author <a href="http://www.agrael.cn">李翔</a>
 * 
 * @param <K>
 *            此映射维护的键类型
 * @param <V>
 *            映射值的类型
 */
@RegisterModule({
	"java.util.Map"
	})
public class ReferenceMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {

	/** 版本号 */
	private static final long serialVersionUID = -6254917146703219097L;

	private final ReferenceKeyType keyReferenceType;
	
	private final ReferenceValueType valueReferenceType;
	
	private transient int initialCapacity = 16;
	
    private transient float loadFactor = 0.75f;
    
    private transient int concurrencyLevel = 16;
	
	private transient ConcurrentMap<Object, Object> map;
	
	public ReferenceMap(){
		this(ReferenceKeyType.WEAK, ReferenceValueType.WEAK);
	}
	
	/**
	 * 通过指定的 key引用类型 和 value引用类型 创建一个带有默认初始容量、加载因子和 concurrencyLevel 的新的空映射。
	 * @param keyReferenceType key引用类型 。
	 * @param valueReferenceType value引用类型。
	 */
	public ReferenceMap(ReferenceKeyType keyReferenceType ,ReferenceValueType valueReferenceType) {
		this.keyReferenceType = keyReferenceType;
		this.valueReferenceType = valueReferenceType;
		map = new ConcurrentHashMap<Object, Object>(initialCapacity,loadFactor,concurrencyLevel);
	}

	/**
	 * 通过指定的 key引用类型 和 value引用类型 创建一个带有指定初始容量、默认加载因子和 concurrencyLevel 的新的空映射。
	 * @param keyReferenceType key引用类型。
	 * @param valueReferenceType value引用类型。
	 * @param initialCapacity 初始容量。该实现执行内部大小调整，以容纳这些元素。
	 */
	public ReferenceMap(ReferenceKeyType keyReferenceType ,ReferenceValueType valueReferenceType,
			int initialCapacity) {
		this.keyReferenceType = keyReferenceType;
		this.valueReferenceType = valueReferenceType;
		this.initialCapacity = initialCapacity;
		map = new ConcurrentHashMap<Object, Object>(initialCapacity,loadFactor,concurrencyLevel);
	}
	
	/**
	 * 通过指定的 key引用类型 和 value引用类型 创建一个带有指定初始容量、加载因子和并发级别的新的空映射。 
	 * @param keyReferenceType key引用类型。
	 * @param valueReferenceType value引用类型。
	 * @param initialCapacity 初始容量。该实现执行内部大小调整，以容纳这些元素。
	 * @param loadFactor 加载因子阈值，用来控制重新调整大小。在每 bin 中的平均元素数大于此阈值时，可能要重新调整大小。
	 * @param concurrencyLevel 当前更新线程的估计数。该实现将执行内部大小调整，以尽量容纳这些线程。
	 */
	public ReferenceMap(ReferenceKeyType keyReferenceType ,ReferenceValueType valueReferenceType,
			int initialCapacity, float loadFactor, int concurrencyLevel) {
		this.keyReferenceType = keyReferenceType;
		this.valueReferenceType = valueReferenceType;
		this.concurrencyLevel = concurrencyLevel;
		this.initialCapacity = initialCapacity;
		this.loadFactor = loadFactor;
		map = new ConcurrentHashMap<Object, Object>(initialCapacity,
				loadFactor, concurrencyLevel);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Set<K> keySet() {
		if(keyReferenceType == ReferenceKeyType.STRONG){
			// 如果为强引用，则 map 中的 keySet 就是需要的
			return (Set<K>) map.keySet();
		}
		// 为其他引用
		return new AbstractSet<K>() {

			public Iterator<K> iterator() {
				return new Iterator<K>() {
					private Iterator<Object> iterator = map.keySet().iterator();
					public boolean hasNext() {
						return iterator.hasNext();
					}

					public K next() {
						return getKey(iterator.next());
					}

					public void remove() {
						iterator.remove();
					}
					
				};
			}

			public int size() {
				return map.size();
			}
			
		};
	}

	@Override
	public void clear() {
		map.clear();
	}

	@Override
	public V remove(Object key) {
		notNull(key);
		Object keyReference = createKeyReferenceWrapper(key);
		Object returnValueReference = map.remove(keyReference);
		return getValue(returnValueReference);
	}

	@Override
	public boolean containsKey(Object key) {
		return map.containsKey(key);
	}

	@Override
	public int size() {
		return map.size();
	}

	@Override
	public boolean isEmpty() {
		return map.isEmpty();
	}

	@Override
	public V put(K key, V value) {
		notNull(key);
		notNull(value);
		Object keyReference = null;
		switch (keyReferenceType) {
		case STRONG:
			keyReference = key;
			break;
		case SOFT:
			keyReference = new FinalizableKeySoftReference<K>(key);
			break;
		case WEAK:
			keyReference = new FinalizableKeyWeakReference<K>(key);
			break;
		case PHANTOM:
			keyReference = new FinalizableKeyPhantomReference<K>(key);
			break;
		}
		Object valueReference = null;
		switch (valueReferenceType) {
		case STRONG:
			valueReference = value;
			break;
		case SOFT:
			valueReference = new FinalizableValueSoftReference<V>(keyReference ,value);
			break;
		case WEAK:
			valueReference = new FinalizableValueWeakReference<V>(keyReference ,value);
			break;
		case PHANTOM:
			valueReference = new FinalizableValuePhantomReference<V>(keyReference ,value);
			break;
		}
		Object returnValueReference = map.put(keyReference, valueReference);
		if(returnValueReference == null){
			return null;
		}
		return getValue(returnValueReference);
	}
	
	
	@SuppressWarnings("unchecked")
	private V getValue(Object valueReference){
		return (V) (valueReferenceType == ReferenceValueType.STRONG ? valueReference : valueReference == null ? null : ((Reference<V>) valueReference).get());
	}
	
	
	@SuppressWarnings("unchecked")
	private K getKey(Object keyReference){
		return (K) (keyReferenceType == ReferenceKeyType.STRONG ? keyReference : keyReference == null ? null : ((Reference<K>) keyReference).get());
	}
	
	/**
	 * 创建 Key 的印射包装器。
	 * @param key 要创建印射包装器的 key。
	 * @return 如果是强引用，返回 key 本身，否则返回  Key 的印射包装器。
	 */
	private Object createKeyReferenceWrapper(Object key){
		return keyReferenceType == ReferenceKeyType.STRONG ? key : new KeyReferenceWrapper(key);
	}
	
	/**
	 * 得到 key 在内存中的地址的 hashCode。
	 * @param key 要得到在内存中的地址的 hashCode 的 key。
	 * @return key 在内存中的地址的 hashCode。
	 * @see System#identityHashCode(Object)
	 */
	private int keyHashCode(Object key){
		return System.identityHashCode(key);
	}
	
	
	private boolean referenceEquals(Reference<?> reference , Object object){
		if(reference == object){
			// 直接引用相同
			return true;
		}
		if(reference == null){
			// 一方为 null 则返回 false（因为上个判断结合这个判断，object 一定不为 null）
			return false;
		}
		if(object instanceof Reference<?>){
			// object 为引用类型，则比较引用类型中的值的引用
			Object referenceValue = ((Reference<?>) object).get();
			return referenceValue != null && reference.get() == referenceValue;
		}
		if(object instanceof KeyReferenceWrapper){
			// object为 key 引用包装类型
			return ((KeyReferenceWrapper) object).getReference() == reference.get();
		}
		return false;
	}

	@Override
	public V get(Object key) {
		notNull(key);
		Object keyReference = createKeyReferenceWrapper(key);
		Object returnValueReference = map.get(keyReference);
		return getValue(returnValueReference);
	}

	public Set<Entry<K, V>> entrySet() {
		return new AbstractSet<Entry<K, V>>() {

			public Iterator<Entry<K, V>> iterator() {
				return new Iterator<Entry<K, V>>() {
					private Iterator<Entry<Object, Object>> iterator = map.entrySet().iterator();

					public boolean hasNext() {
						return iterator.hasNext();
					}

					public Entry<K, V> next() {
						final Entry<Object, Object> entry = iterator.next();
						return new Entry<K, V>() {

							public K getKey() {
								return ReferenceMap.this.getKey(entry.getKey());
							}

							public V getValue() {
								return ReferenceMap.this.getValue(entry.getValue());
							}

							public V setValue(V value) {
								throw new UnsupportedOperationException("不支持的操作。");
							}

						};
					}

					public void remove() {
						iterator.remove();
					}

				};
			}

			public int size() {
				return map.size();
			}

		};
	}
	
	/**
	 * 弱引用对象的键类。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 * @param <T> 引用类型
	 */
	protected class FinalizableKeyWeakReference<T> extends FinalizableWeakReference<T> implements FinalizableReference<T>{

		private final int hashCode;
		
		/**
		 * 构造一个新的 弱引用对象的键类 实例。
		 * @param referent 键。
		 */
		protected FinalizableKeyWeakReference(T referent) {
			super(referent);
			this.hashCode = keyHashCode(referent);
		}

		public void finalizeReferent() {
			map.remove(this);
		}

		@Override
		public int hashCode() {
			return hashCode;
		}

		@Override
		public boolean equals(Object object) {
			return referenceEquals(this, object);
		}
	}
	
	/**
	 * 弱引用对象的值类。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 * @param <T> 引用类型
	 */
	protected class FinalizableValueWeakReference<T> extends FinalizableWeakReference<T> implements FinalizableReference<T>{
		
		private Object keyReference;
		
		/**
		 * 构造一个新的 弱引用对象的值类 实例。
		 * @param keyReference 与此值关联的 键 。
		 * @param referent 值。
		 */
		protected FinalizableValueWeakReference(Object keyReference ,T referent) {
			super(referent);
			this.keyReference = keyReference;
		}
		
		public void finalizeReferent() {
			map.remove(keyReference,this);
		}
	}
	
	/**
	 * 软引用对象的键类。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 * @param <T> 引用类型
	 */
	protected class FinalizableKeySoftReference<T> extends FinalizableSoftReference<T> implements FinalizableReference<T>{

		private final int hashCode;
		
		/**
		 * 构造一个新的 软引用对象的键类 实例。
		 * @param referent 键。
		 */
		protected FinalizableKeySoftReference(T referent) {
			super(referent);
			this.hashCode = keyHashCode(referent);
		}

		public void finalizeReferent() {
			map.remove(this);
		}

		@Override
		public int hashCode() {
			return hashCode;
		}

		@Override
		public boolean equals(Object object) {
			return referenceEquals(this, object);
		}
	}
	
	/**
	 * 软引用对象的值类。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 * @param <T> 引用类型
	 */
	protected class FinalizableValueSoftReference<T> extends FinalizableSoftReference<T> implements FinalizableReference<T>{
		
		private Object keyReference;
		
		/**
		 * 构造一个新的 软引用对象的值类 实例。
		 * @param keyReference 与此值关联的 键 。
		 * @param referent 值。
		 */
		protected FinalizableValueSoftReference(Object keyReference ,T referent) {
			super(referent);
			this.keyReference = keyReference;
		}
		
		public void finalizeReferent() {
			map.remove(keyReference,this);
		}
	}
	

	
	/**
	 * 虚引用对象的键类。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 * @param <T> 引用类型
	 */
	protected class FinalizableKeyPhantomReference<T> extends FinalizablePhantomReference<T> implements FinalizableReference<T>{

		private final int hashCode;
		
		/**
		 * 构造一个新的 虚引用对象的键类 实例。
		 * @param referent 键。
		 */
		protected FinalizableKeyPhantomReference(T referent) {
			super(referent);
			this.hashCode = keyHashCode(referent);
		}

		public void finalizeReferent() {
			map.remove(this);
		}

		@Override
		public int hashCode() {
			return hashCode;
		}

		@Override
		public boolean equals(Object object) {
			return referenceEquals(this, object);
		}
	}
	
	/**
	 * 虚引用对象的值类。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 * @param <T> 引用类型
	 */
	protected class FinalizableValuePhantomReference<T> extends FinalizablePhantomReference<T> implements FinalizableReference<T>{
		
		private Object keyReference;
		
		/**
		 * 构造一个新的 虚引用对象的值类 实例。
		 * @param keyReference 与此值关联的 键 。
		 * @param referent 值。
		 */
		protected FinalizableValuePhantomReference(Object keyReference ,T referent) {
			super(referent);
			this.keyReference = keyReference;
		}
		
		public void finalizeReferent() {
			map.remove(keyReference,this);
		}
	}
	
	/**
	 * 引用类型中的对象的另一个包装器，该包装器实现为引用类型中的对象保持 put 与 get 时用到的 hashCode 和 equals 方法的一致性。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 */
	protected static class ReferenceWrapper{
		
		protected final Object reference;
		
		/**
		 * 使用一个引用类型中的对象实例构造一个新的包装器。
		 * @param reference 要包装的引用类型中的对象实例。
		 */
		protected ReferenceWrapper(Object reference) {
			this.reference = reference;
		}

		/**
		 * 得到被包装的对象。
		 * @return 被包装的对象。
		 */
		protected Object getReference() {
			return reference;
		}

		/**
		 * 调用包装对象的 hashCode 方法。
		 * @return 包装对象的 hashCode。
		 */
		@Override
		public int hashCode() {
			return reference.hashCode();
		}

		/**
		 * 调用包装对象的  equals 方法比较。
		 * @param obj 要与之比较的对象。
		 * @return 如果传入的对象和包装对象“相等”则返回 true，否则返回 false。
		 */
		@Override
		public boolean equals(Object obj) {
			// 使用包装对象的 equals
			return reference.equals(obj);
		}
	}
	
	/**
	 * 专用于  key 的引用类型中的对象的另一个包装器。该包装器重写了 hashCode 方法，使用 {@link System#identityHashCode(Object)} 取得 hash值 。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 */
	protected static class KeyReferenceWrapper extends ReferenceWrapper {

		/**
		 * 使用一个引用类型中的 key对象实例 构造一个新的包装器。
		 * @param reference 要包装的引用类型中的key对象实例。
		 */
		protected KeyReferenceWrapper(Object reference) {
			super(reference);
		}
		
		/**
		 * 返回包装的 Key 的内存里的 hashCode。
		 * @return 包装的 Key 的内存里的 hashCode。
		 * @see System#identityHashCode(Object)
		 */
		@Override
		public int hashCode() {
			// 这里要认为同一内存地址的才是相同对象，所以取得内存地址中的 HASH码
			return System.identityHashCode(reference);
		}
	}
	
	/**
	 * 用作 K 的引用类型。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 */
	public static enum ReferenceKeyType {
		
		/** 强引用 */
		STRONG,
		
		/** 软引用 */
		SOFT,
		
		/** 弱引用 */
		WEAK,
		
		/** 虚引用 */
		PHANTOM;
		
	}
	
	/**
	 * 用作 V 的引用类型。
	 * @author <a href="http://www.agrael.cn">李翔</a>
	 *
	 */
	public static enum ReferenceValueType {
		
		/** 强引用 */
		STRONG,
		
		/** 软引用 */
		SOFT,
		
		/** 弱引用 */
		WEAK,
		
		/** 虚引用 */
		PHANTOM;
	}
	
	private void writeObject(ObjectOutputStream out) throws IOException  {
	    out.defaultWriteObject();
	    out.writeInt(size());
	    out.writeFloat(loadFactor);
	    out.writeInt(concurrencyLevel);
	    for (Map.Entry<Object, Object> entry : map.entrySet()) {
	      Object key = getKey(entry.getKey());
	      Object value = getValue(entry.getValue());

	      if (key != null && value != null) {
	        out.writeObject(key);
	        out.writeObject(value);
	      }
	    }
	    out.writeObject(null);
	  }

	  @SuppressWarnings("unchecked")
	private void readObject(ObjectInputStream in) throws IOException,
	      ClassNotFoundException {
	    in.defaultReadObject();
	    int size = in.readInt();
	    float loadFactor = in.readFloat();
	    int concurrencyLevel = in.readInt();
	    this.initialCapacity = size;
	    this.map = new ConcurrentHashMap<Object, Object>(size,loadFactor,concurrencyLevel);
	    while (true) {
	      K key = (K) in.readObject();
	      if (key == null) {
	        break;
	      }
	      V value = (V) in.readObject();
	      put(key, value);
	    }
	  }
	  
	  private static void notNull(Object obj){
		  if(obj == null){
			  throw new IllegalArgumentException();
		  }
	  }
}
